;CF128A
          .TITLE    "Apple /// CompactFlash/IDE Driver Ver 1.28A"
                    ;Limit Maximum Volume Size to 32767 Blocks
                    ;LoBuffer & HiBuffer zero page pointers

          .PROC     CFIDE

                    ; SOS Equates
Extpg     .EQU      1401             ;driver extended bank address offset
AllocSIR  .EQU      1913             ;allocate system internal resource
DeAlcSIR  .EQU      1916             ;de-allocate system internal resource
SysErr    .EQU      1928             ;report error to system
EReg      .EQU      0FFDF            ;environment register

ReqCode   .EQU      0C0              ;request code
SosUnit   .EQU      0C1              ;unit number
SosBuf    .EQU      0C2              ;SOS buffer pointer
ReqCnt    .EQU      0C4              ;requested byte count
CtlStat   .EQU      0C2              ;control/status code
CSList    .EQU      0C3              ;control/status list pointer
SosBlk    .EQU      0C6              ;starting block number
QtyRead   .EQU      0C8              ;bytes read return by D_READ

                    ; Our temps in zero page
CurPart   .EQU      00CC
FR_Code   .EQU      00CD             ;1 bytes
Count     .EQU      00CD             ;2 bytes

                    ;Parameter block specific to current SOS request
ATA_Cmd   .EQU      00CF
Drv_Parm  .EQU      00D0
Sect_HB   .EQU      00D1
Sect_MB   .EQU      00D2
Sect_LB   .EQU      00D3
Num_Blks  .EQU      00D4             ;2 bytes lb,hb
LoBuffer  .EQU      00D6             ;Low Byte Buffer Pointer
HiBuffer  .EQU      00D8             ;High Byte Buffer Pointer
CurDrive  .EQU      00DA             ;Current IDE drive number of SosUnit #
CurDrvNo  .EQU      00DB             ;Current DIB drive number of SosUnit #

                    ; SOS Error Codes
XREQCODE  .EQU      020              ;Invalid request code
XCTLCODE  .EQU      021              ;Invalid control/status code
XCTLPARAM .EQU      022              ;Invalid control/status parameter
XNORESRC  .EQU      025              ;Resource not available
XBADOP    .EQU      026              ;Invalid operation
XIOERROR  .EQU      027              ;I/O error
XNODRIVE  .EQU      028              ;Drive not connected
XBYTECNT  .EQU      02C              ;Byte count not a multiple of 512
XBLKNUM   .EQU      02D              ;Block number to large
XDCMDERR  .EQU      031              ;device command ABORTED error occurred
XCKDEVER  .EQU      032              ;Check device readiness routine failed
XNORESET  .EQU      033              ;Device reset failed
XNODEVIC  .EQU      038              ;Device not connected

                    ;IFC I/O locations
IOBase    .EQU      0C080
ATdataHB  .EQU      IOBase+0.
SetCSMsk  .EQU      IOBase+1.        ;Two special strobe locations to set and
                                     ;clear MASK bit that is used to disable
                                     ;CS0 line to the CompactFlash during the
                                     ;CPU read cycles.
ClrCSMsk  .EQU      IOBase+2.        ;that occur before every CPU write cycle.
                                     ;The normally innocuous read cycles were
                                     ;causing the SanDisk CF to double
                                     ;increment during sector writes commands.

Alt_Stat  .EQU      IOBase+6.        ;when reading (not used)
ATAdCtrl  .EQU      IOBase+6.        ;when writing
ATdataLB  .EQU      IOBase+8.
ATAError  .EQU      IOBase+9.
Features  .EQU      IOBase+9.
ATSectCt  .EQU      IOBase+10.
ATSector  .EQU      IOBase+11.       ;11=LB,12=MB,13=HB
ATAHead   .EQU      IOBase+14.
ATCmdReg  .EQU      IOBase+15.       ;when writing
ATA_Stat  .EQU      IOBase+15.       ;when reading

                    ; ATA/CF Commands Codes
ATA_XErr  .EQU      003
ATACRead  .EQU      020
ATACWrit  .EQU      030
ATA_Vrfy  .EQU      040
ATA_Frmt  .EQU      050
ATA_Diag  .EQU      090
ATAIdent  .EQU      0EC
SetFeatr  .EQU      0EF

                    ;Constants for Wait
Wait5ms   .EQU      42.
Wait150ms .EQU      242.

                    ; Switch Macro
          .MACRO    switch
          .IF       "%1" <> ""       ;if parameter 1 is present
          LDA       %1               ;load A with switch index
          .ENDC
          CMP       #%2+1            ;do bounds check
          BCS       $010
          ASL    A
          TAY
          LDA       %3+1,Y           ;get switch index from table
          PHA
          LDA       %3,Y
          PHA
          .IF       "%4" <> "*"      ;if parameter 4 omitted,
          RTS       ;go to code
          .ENDC
$010      .ENDM

          .WORD     0FFFF       ;Comment Field
          .WORD     71.
          .ASCII    "Apple /// CFFA Driver (all versions) - "
          .ASCII    "written by Dale S. Jackson  6/08"

                    ;Device identification Block (DIB) - Volume #0
DIB_0     .WORD     DIB_1            ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE1        ";device name

          .BYTE     0C0              ;active, page aligned
DIB0_Slot .BYTE     0FF              ;slot number
          .BYTE     000              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB0_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     128A             ;Version 1.28A
          .WORD     0003             ;DCB length followed by DCB
Driv_No0  .BYTE     000              ;Drive #, Bit 0=$0 (master) or $1 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
Part_No0  .BYTE     000              ;Partition number on the drive = $00-07
PIO_Mode  .BYTE     000              ;PIO Transfer Mode (mode value plus $08)
                                     ;A value $00 turns off setting the PIO
                                     ;transfer mode during driver partition
                                     ;initialization.

                                     ;DIB Values for Map & Drive No.
                    ;Map No.   IDE $0   IDE $1     Map No.   IDE $0    IDE $1
                    ;   0       $00       $01        32        $80        $81
                    ;   1       $04       $05        33        $84        $85
                    ;   2       $08       $09        34        $88        $89
                    ;   3       $0C       $0D        35        $8C        $8D
                    ;   4       $10       $11        36        $90        $91
                    ;   5       $14       $15        37        $94        $95
                    ;   6       $18       $19        38        $98        $99
                    ;   7       $1C       $1D        39        $9C        $9D
                    ;   8       $20       $21        40        $A0        $A1
                    ;   9       $24       $25        41        $A4        $A5
                    ;  10       $28       $29        42        $A8        $A9
                    ;  11       $2C       $2D        43        $AC        $AD
                    ;  12       $30       $31        44        $B0        $B1
                    ;  13       $34       $35        45        $B4        $B5
                    ;  14       $38       $39        46        $B8        $B9
                    ;  15       $3C       $3D        47        $BC        $BD
                    ;  16       $40       $41        48        $C0        $C1
                    ;  17       $44       $45        49        $C4        $C5
                    ;  18       $48       $49        50        $C8        $C9
                    ;  19       $4C       $4D        51        $CC        $CD
                    ;  20       $50       $51        52        $D0        $D1
                    ;  21       $54       $55        53        $D4        $D5
                    ;  22       $58       $59        54        $D8        $D9
                    ;  23       $5C       $5D        55        $DC        $DD
                    ;  24       $60       $61        56        $E0        $E1
                    ;  25       $64       $65        57        $E4        $E5
                    ;  26       $68       $69        58        $E8        $E9
                    ;  27       $6C       $6D        59        $EC        $ED
                    ;  28       $70       $71        60        $F0        $F1
                    ;  29       $74       $75        61        $F4        $F5
                    ;  30       $78       $79        62        $F8        $F9
                    ;  31       $7C       $7D        63        $FC        $FD

                    ;Device identification Block (DIB) - Volume #1
                    ;Page alignment begins here
DIB_1     .WORD     DIB_2            ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE2        ";device name

          .BYTE     080              ;active
          .BYTE     0FF              ;slot number
          .BYTE     001              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB1_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     128A             ;Version 1.28A
          .WORD     0002             ;DCB length followed by DCB
          .BYTE     000              ;Drive # = $00 (master) or $01 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
          .BYTE     001              ;Partition number on the drive = $00-07

                    ;Device identification Block (DIB) - Volume #2
DIB_2     .WORD     DIB_3            ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE3        ";device name

          .BYTE     080              ;active
          .BYTE     0FF              ;slot number
          .BYTE     002              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB2_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     128A             ;Version 1.28A
          .WORD     0002             ;DCB length followed by DCB
          .BYTE     000              ;Drive # = $00 (master) or $01 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
          .BYTE     002              ;Partition number on the drive = $00-07

                    ;Device identification Block (DIB) - Volume #3
DIB_3     .WORD     DIB_4            ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE4        ";device name

          .BYTE     080              ;active
          .BYTE     0FF              ;slot number
          .BYTE     003              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB3_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     128A             ;Version 1.28A
          .WORD     0002             ;DCB length followed by DCB
          .BYTE     000              ;Drive # = $00 (master) or $01 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
          .BYTE     003              ;Partition number on the drive = $00-07

                    ;Device identification Block (DIB) - Volume #4
DIB_4     .WORD     DIB_5            ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE5        ";device name

          .BYTE     080              ;active
          .BYTE     0FF              ;slot number
          .BYTE     004              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB4_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     128A             ;Version 1.28A
          .WORD     0002             ;DCB length followed by DCB
          .BYTE     000              ;Drive # = $00 (master) or $01 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
          .BYTE     004              ;Partition number on the drive = $00-07

                    ;Device identification Block (DIB) - Volume #5
DIB_5     .WORD     DIB_6            ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE6        ";device name

          .BYTE     080              ;active
          .BYTE     0FF              ;slot number
          .BYTE     005              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB5_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     128A             ;Version 1.28A
          .WORD     0002             ;DCB length followed by DCB
          .BYTE     000              ;Drive # = $00 (master) or $01 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
          .BYTE     005              ;Partition number on the drive = $00-07

                    ;Device identification Block (DIB) - Volume #6
DIB_6     .WORD     DIB_7            ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE7        ";device name

          .BYTE     080              ;active
          .BYTE     0FF              ;slot number
          .BYTE     006              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB6_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     128A             ;Version 1.28A
          .WORD     0002             ;DCB length followed by DCB
          .BYTE     000              ;Drive # = $00 (master) or $01 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
          .BYTE     006              ;Partition number on the drive = $00-07

                    ;Device identification Block (DIB) - Volume #7
DIB_7     .WORD     0000             ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE8        ";device name

          .BYTE     080              ;active
          .BYTE     0FF              ;slot number
          .BYTE     007              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB7_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     128A             ;Version 1.28A
          .WORD     0002             ;DCB length followed by DCB
          .BYTE     000              ;Drive # = $00 (master) or $01 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
          .BYTE     007              ;Partition number on the drive = $00-07

                    ;Read_Blk - Read requested blocks from device into memory
Read_Blk  JSR       SetupLBA         ;Program the device's task file registers
          LDA       ATA_Cmd
          STA       ATCmdReg,x       ;Issue the read, verify, identity command
ReadIt    LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       ReadIt
          AND       #009             ;Check for error response from device
          CMP       #008             ;If DRQ=0 or ERR=1 a device error
          BNE       Read_Err
          PHP
          SEI                        ;No interuptions while I get the sector
          BEQ       $2
                    ;**** This segment must not cross page boundary ****
$1        LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       $1
$2        LDA       ATdataLB,x
          STA       (LoBuffer),y
          INY
          LDA       ATdataHB,x
          STA       (LoBuffer),y
          INY
          BNE       $1
          INC       LoBuffer+1
$3        LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       $3
          LDA       ATdataLB,x
          STA       (LoBuffer),y
          INY
          LDA       ATdataHB,x
          STA       (LoBuffer),y
          INY
          BNE       $3
                    ;**** End of segment ****
          PLP
          JSR       IncrAdr
          DEC       Num_Blks         ;did we get what was asked for
          BNE       ReadIt
          DEC       Num_Blks+1
          BPL       ReadIt
          JMP       CSet2Mhz
Read_Err  JMP       Save_Err

                    ;D_WRITE call processing
DWrite    JSR       CkCnt
          LDA       #ATACWrit
          STA       ATA_Cmd
CWrite    LDA       Num_Blks         ;check for Num_Blks greater than zero
          ORA       Num_Blks+1
          BEQ       WritDone         ;quantity to write is zero
                    ; WriteBlk - Write requested blocks from memory to device
          JSR       FixUp
          JSR       SetupLBA         ;Program the device's task file registers
          LDA       ATA_Cmd
          STA       ATCmdReg,x       ;Issue the write/format command to drive
WriteIt   BCC       $1               ;If carry clear, buffer pointers unchanged
          CLC
          LDA       LoBuffer
          ADC       #001
          STA       HiBuffer
          LDA       LoBuffer+1
          ADC       #000
          STA       HiBuffer+1
          LDA       LoBuffer+ExtPG
          STA       HiBuffer+ExtPG
$1        PHP
          SEI                        ;No interuptions while I get the sector
$2        LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       $2
          AND       #029             ;Check for error response from device
          CMP       #008             ;If DF=1 or DRQ=0 or ERR=1 a device error
          BNE       Writ_Err
                    ;**** This segment must not cross page boundary ****
$3        LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       $3
          LDA       SetCSMsk,x       ;any access sets mask bit to block
                                     ;IDE -CS0 on I/O read to drive
          LDA       (HiBuffer),y
          STA       ATdataHB,x
          LDA       (LoBuffer),y
          STA       ATdataLB,x
          LDA       ClrCSMsk,x       ;Set back to normal, allow CS0 assertions
                                     ;on read cycles
          INY
          INY
          BNE       $3
          INC       LoBuffer+1
          INC       HiBuffer+1
$4        LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       $4
          LDA       SetCSMsk,x       ;any access sets mask bit to block
                                     ;IDE -CS0 on I/O read to drive
          LDA       (HiBuffer),y
          STA       ATdataHB,x
          LDA       (LoBuffer),y
          STA       ATdataLB,x
          LDA       ClrCSMsk,x       ;Set back to normal, allow CS0 assertions
                                     ;on read cycles
          INY
          INY
          BNE       $4
                    ;**** End of segment ****
          PLP
          JSR       BumpAdr
          DEC       Num_Blks         ;did we do what was asked for
          BNE       WriteIt
          DEC       Num_Blks+1       ;we might have to do this one more time
          BPL       WriteIt
WritDone  JMP       CSet2Mhz         ;exit write routines
Writ_Err  JSR       Save_Err
          LDA       #XIOERROR        ;I/O error
          JSR       SysErr           ;doesn't return

                    ; local storage locations
SlotCX    .BYTE     000              ;compute C0X0 and store on init

PtBlkIdx  .BYTE     0A3,0A8,0B3,0B8,0C3,0C8,0D3,0D8  ;Offsets to 8 block
PtVolIdx  .BYTE     0A6,0AB,0B6,0BB,0C6,0CB,0D6,0DB  ;segment for ea partition

UnitStat  .BLOCK    008,0FF    ;if UnitStat,X = $FF then partition info
                                                ;not initialized
                               ;if UnitStat,X > $0F then UnitStat = error code
                               ;else UnitStat,X = partition number
                               ;if UnitStat,0 = #XNODRIVE then driver is
                                                ;nonfunctional

LastOP    .BLOCK    008,0FF          ;last op for D_REPEAT calls

StBlk_HB  .BLOCK    008,000          ;Starting block number for SOS/ProDos
StBlk_MB  .BLOCK    008,000          ;vol #0, vol #1, vol #2, vol #3
StBlk_LB  .BLOCK    008,000          ;vol #4, vol #5, vol #6, vol #7

Block_LB  .BLOCK    008,000          ;Total blocks for each volume #
Block_HB  .BLOCK    008,000

DCB_Idx   .BYTE     000
          .BYTE     DIB1_Blks-DIB0_Blks
          .BYTE     DIB2_Blks-DIB0_Blks
          .BYTE     DIB3_Blks-DIB0_Blks
          .BYTE     DIB4_Blks-DIB0_Blks
          .BYTE     DIB5_Blks-DIB0_Blks
          .BYTE     DIB6_Blks-DIB0_Blks
          .BYTE     DIB7_Blks-DIB0_Blks

SIR_Addr  .WORD     SIR_Tbl
SIR_Tbl   .BLOCK    005,000
SIR_Len   .EQU      *-SIR_Tbl

PmapCall  .BLOCK    003,000          ;Block $hb mb lb for partition record
          .BYTE     001,000          ;Read 1 ATA sector (block) lb, hb
          .WORD     PmapBuf          ;Buffer address to place partition record

PmapBuf   .BLOCK    0100,000
Err_Data  .BLOCK    0100,000

          .ASCII    "Written By Dale S. Jackson, initial writing 12/12/02"
          .ASCII    "v1.28A revised 6/27/08"

                    ; Main entry point
Entry     LDA       UnitStat+0
          CMP       #XNODEVIC
          BEQ       No_Drive
          LDX       SosUnit          ;get drive number for this unit
          LDY       DCB_Idx,X
          LDA       Driv_No0,Y
          STA       CurDrvNo
          AND       #001             ;only bit 0 counts
          STA       CurDrive         ;Set device to LBA mode
          ORA       #00E             ;device mode bits   %00001(LBA)1(Drive#)
          ASL       A                ;shift left 4 bits to high order nibble
          ASL       A
          ASL       A
          ASL       A
          STA       Drv_Parm
          LDA       ReqCode
          CMP       #002             ;Status Call
          BCS       $1
          JSR       InitPmap         ;Check if partition table is current
                                     ;fetch it if not.
$1        JSR       Dispatch         ;Now call the dispatcher
          LDX       SosUnit          ;Save current operation
          LDA       ReqCode          ;for D_REPEAT processing
          STA       LastOP,X
          RTS       ;Bye

                    ;The Dispatcher.  Does it depending on ReqCode.  Note
                    ;that if we came in on a D_INIT call, we do a branch to
                    ;Dispatch, normally.  Dispatch is called as a subroutine!
                    ;We copy the buffer pointer and block # from the parameter
                    ;area into our own temps, as the system seems to want them
                    ;left ALONE.

Dispatch  SWITCH    ReqCode,9,DoTable  ;go do it.

BadReq    LDA       #XREQCODE        ;bad request code!
          JSR       SysErr           ;doesn't return

Slot_Err  LDA       #XNODEVIC        ;#XNORESRC        SIR not available
          STA       UnitStat+0       ;no! it didn't go ok.
No_Drive  LDA       #XNODRIVE        ;Flag this driver not useable
Err_Out1  JSR       SysErr           ;doesn't return

                    ;D_INIT call processing for all Volumes.
                    ;Called at system init time only.  Check DIB0_Slot to
                    ;make sure that the user set a valid slot number for our
                    ;interface.  Allocate it by calling AllocSIR. If slot not
                    ;available then set UnitStat+0 to XNORESRC error code.

DInit     LDA       SIR_Tbl
          BNE       Norm_Out
          LDA       DIB0_Slot
          AND       #007

                    ;Compute the system internal resource number (SIR) and
                    ;call AllocSIR to try and grab that for us.  It performs
                    ;slot checking as a side effect.

          ORA       #010             ;SIR = 16+slot#
          STA       SIR_Tbl
          LDA       #SIR_Len
          LDX       SIR_Addr
          LDY       SIR_Addr+1
          JSR       AllocSIR         ;this one's mine!
          BCS       Slot_Err

          LDA       DIB0_Slot        ;Compute C0X0 for this slot
          ASL   A
          ASL   A
          ASL   A
          ASL   A
          STA       SlotCX
          JSR       ResetIFC         ;Initialization of Device
          BCS       Slot_Err
          LDY       #007             ;Reset drive status to initial startup
          LDA       #0FF
$1        STA       UnitStat,Y
          DEY
          BPL       $1
Norm_Out  CLC
          RTS

          .INCLUDE  CF128B
          .END

;CF128B
                    ; Initialize Partition Table Data
InitPmap  LDX       SosUnit
          LDA       UnitStat,X
          CMP       #0FF
          BNE       $2

                    ;Set ATA transfer mode for drive
          LDA       PIO_Mode
          BEQ       $1               ;If mode=$0 then skip setting xfer mode
          LDA       #003             ;CF Feature $03 = set transfer mode
          STA       FR_Code
          JSR       D_Featur

$1        JSR       GetPmap
          JSR       PInit
$2        CMP       #010             ;Check if Unit driver is good.
          BCS       Err_Out1
          RTS

                    ; Dispatch table.  One entry per command number, with holes.
DoTable   .WORD     DRead-1          ;0 read request
          .WORD     DWrite-1         ;1 write request
          .WORD     DStatus-1        ;2 status request
          .WORD     DControl-1       ;3 control request
          .WORD     BadReq-1         ;4 unused
          .WORD     BadReq-1         ;5 unused
          .WORD     BadOp-1          ;6 open - invalid request
          .WORD     BadOp-1          ;7 close - invalid request
          .WORD     DInit-1          ;8 init request
          .WORD     DRepeat-1        ;9 repeat request

                    ;D_REPEAT - repeat the last D_READ or D_WRITE call
DRepeat   LDX       SosUnit
          LDA       LastOP,X         ;look at the last thing we did
          CMP       #002
          BCS       BadOp
          STA       ReqCode
          JMP       Dispatch

BadOp     LDA       #XBADOP          ;invalid operation!
          JSR       SysErr           ;doesn't return

                    ;Get Partition Map from drive
GetPmap   LDA       CurDrvNo
          AND       #0FC             ;get upper 6 bits of drive number for
                                     ;partition address.
          STA       PmapCall
          LDY       #006
$1        LDA       PmapCall,Y
          STA       Sect_HB,Y
          DEY
          BPL       $1
          LDA       #000
          STA       LoBuffer+Extpg
          LDA       #ATACRead
          STA       ATA_Cmd
          JMP       SRead

                    ;Initialize partition info for each partition in the
                    ;current table.
PInit     LDX       #007
$1        LDY       DCB_Idx,X
          LDA       CurDrvNo
          CMP       Driv_No0,Y
          BNE       $4
          LDA       Part_No0,Y       ;Get assigned partition number this driver
          CMP       #008
          BCS       $2               ;Partition number is out of range
          TAY
          STY       CurPart
          JSR       GStrtBlk
          BCS       $2
          JSR       GVolSize
          BCS       $2
          LDY       DCB_Idx,X
          LDA       Block_HB,X
          STA       DIB0_Blks+1,Y
          LDA       Block_LB,X
          STA       DIB0_Blks,Y
          LDA       CurPart
          BPL       $3
$2        LDA       #XNODRIVE        ;Flag this driver not useable
$3        STA       UnitStat,X
$4        DEX
          BPL       $1
          CLC
          RTS

GStrtBlk  LDA       PtBlkIdx,Y       ;Xreg contains DIB unit number,
          TAY                        ;Yreg contains drive partition count
          LDA       PmapBuf+2,Y
          CMP       #0FF             ;Test if beginning track is valid
          BNE       $1
          CMP       PmapBuf+1,Y
          BNE       $1
          CMP       PmapBuf,Y
          BEQ       Bad_Ptn          ;Return with carry set
$1        ORA       PmapCall         ;set upper 6 bits for partition address
          STA       StBlk_HB,X
          LDA       PmapBuf+1,Y
          STA       StBlk_MB,X
          ORA       PmapBuf+2,Y      ;Check if beginning track is zero
          ORA       PmapBuf,Y
          BEQ       Bad_Ptn          ;Return with carry set if zero
          LDA       PmapBuf,Y
          STA       StBlk_LB,X
          CLC
          RTS
Bad_Ptn   SEC
          RTS

GVolSize  LDY       CurPart          ;Xreg contains DIB unit number
          LDA       PtVolIdx,Y
          TAY
          LDA       PmapBuf,Y
          STA       Block_LB,X
          ORA       PmapBuf+1,Y
          BEQ       Bad_Ptn          ;Volume size cannot be zero
          LDA       PmapBuf+1,Y
          STA       Block_HB,X
          BMI       Bad_Ptn          ;Volume size is greater than 32767 blocks
          CLC
          RTS

                    ; Random support and checking routines for the block driver
                    ; Check ReqCnt to insure it's a multiple of 512.

CkCnt     LDA       ReqCnt           ;look at the lsb of bytes to do
          BNE       $1               ;no good!  lsb should be 00
          STA       Num_Blks+1       ;zero high byte of number of blocks
          LDA       ReqCnt+1         ;look at the msb
          LSR       A                ;put bottom bit into carry, 0 into top
          STA       Num_Blks         ;save as number of blocks to transfer
          BCC       CVTBLK           ;Carry is set from LSR to mark error.
$1        LDA       #XBYTECNT
          JSR       SysErr           ;doesn't return

                    ;Test for valid block number.Carry clear on return means
                    ;no error.  Carry set means block number bad.  X register
                    ;contains volume number.

CVTBLK    LDA       SosBuf
          STA       LoBuffer
          LDA       SosBuf+1
          STA       LoBuffer+1
          LDA       SosBuf+Extpg
          STA       LoBuffer+Extpg

          LDY       SosUnit
          LDA       SosBlk
          CMP       Block_LB,y
          LDA       SosBlk+1
          SBC       Block_HB,y
          BCC       $2

          LDA       #XBLKNUM
          JSR       SysErr           ;doesn't return

$2        LDA       SosBlk
          ADC       StBlk_LB,y
          STA       Sect_LB
          LDA       SosBlk+1
          ADC       StBlk_MB,y
          STA       Sect_MB
          LDA       StBlk_HB,y
          ADC       #000
          STA       Sect_HB
          RTS

IncrAdr   INC       Count+1
          INC       Count+1
BumpAdr   INC       LoBuffer+1         ;increment LoBuffer msb

                    ;Fix up the buffer pointer to correct for an addressing
                    ;anomalies!  We just need to do the initial checking
                    ;for two cases:
                    ;00xx bank N -> 80xx bank N-1
                    ;20xx bank 8F if N was 0
                    ;FDxx bank N -> 7Dxx bank N+1
                    ;If pointer is adjusted, return with carry set

FixUp     LDA       LoBuffer+1         ;look at msb
          BEQ       $1                 ;that's one!
          CMP       #0FD               ;is it the other one?
          BCS       $2                 ;yep. fix it!
          RTS                          ;Pointer unchanged, return carry clear.
$1        LDA       #080               ;00xx -> 80xx
          STA       LoBuffer+1
          DEC       LoBuffer+Extpg     ;bank N -> band N-1
          LDA       LoBuffer+Extpg     ;see if it was bank 0
          CMP       #07F               ;(80) before the DEC.
          BNE       $3                 ;nope! all fixed.
          LDA       #020               ;if it was, change both
          STA       LoBuffer+1         ;msb of address and
          LDA       #08F
          STA       LoBuffer+Extpg     ;bank number for bank 8F
          RTS                          ;return carry set
$2        AND       #07F               ;strip off high bit
          STA       LoBuffer+1         ;FDxx ->7Dxx
          INC       LoBuffer+Extpg     ;bank N -> bank N+1
$3        RTS                          ;return carry set

                    ; D_READ call processing
DRead     JSR       CkCnt
          LDA       #ATACRead
          STA       ATA_Cmd
CRead     LDA       #000             ;Zero # bytes read
          STA       Count
          STA       Count+1
          TAY
          STA       (QtyRead),Y      ;bytes read
          INY
          STA       (QtyRead),Y      ;msb of bytes read
          LDA       Num_Blks         ;check for Num_Blks greater than zero
          ORA       Num_Blks+1
          BEQ       ReadDone
$1        JSR       FixUp
          JSR       Read_Blk         ;Transfer a block to/from the disk
          LDY       #000
          LDA       count
          STA       (QtyRead),y      ;Update # of bytes actually read
          INY
          LDA       count+1
          STA       (QtyRead),y
          BCS       IO_Error         ;An error occurred
ReadExit  RTS                        ;exit read routines
ReadDone  JMP       CSet2Mhz

SRead     JSR       Read_Blk         ;Transfer a block to/from the disk
          BCC       ReadExit
IO_Error  LDA       #XIOERROR        ;I/O error
          JSR       SysErr           ;doesn't return

Save_Err  LDA       ATA_Stat,x
          STA       Err_Data
          LSR       A
          BCS       $1               ;if status ERR bit is one then get error
          LDA       #000             ;register data,else return error data
          BEQ       $2               ;byte = zero.
$1        LDA       ATAError,x
$2        STA       Err_Data+1
          LDA       ATSector,x       ;retrieve sector # error occurred
          STA       Err_Data+2       ;LB
          LDA       ATSector+1,x
          STA       Err_Data+3       ;MB
          LDA       ATSector+2,x
          STA       Err_Data+4       ;HB
          LDA       ATSectCt,x
          STA       Err_Data+5       ;retrieve # of sectors left
          STY       Count
          SEC
          JMP       Set2Mhz

                    ;SetupLBA - Programs devices task registers with LBA data
                    ;Input:
                    ;       partition data Sect_HB, MB, & LB
                    ;       X =  requested slot number in form $n0 where
                    ;            n =  slot 1 to 7
                    ;This function programs the device registers with
                    ;the ATA Logical Block Address (LBA) to be accessed.
                    ;A SOS block and a ATA sector are both 512 bytes.
                    ;Logical Block Mode, the Logical Block Address is
                    ;interpreted as follows:
                    ; LBA07-LBA00: Sector Number Register D7-D0.
                    ; LBA15-LBA08: Cylinder Low Register D7-D0.
                    ; LBA23-LBA16: Cylinder High Register D7-D0.
                    ; LBA27-LBA24: Drive/Head Register bits HS3-HS0.

SetupLBA  JSR       CkDevice         ;returns with carry set
          LDA       Sect_LB
          STA       ATSector,x       ;store low block # into LBA 0-7
          LDA       Sect_MB
          STA       ATSector+1,x     ;store mean block # into LBA 15-8
          LDA       Sect_HB
          STA       ATSector+2,x     ;store high block # LBA bits 23-16
          LDA       Num_Blks
          STA       ATSectCt,x       ;store # of blocks to be read/written
          LDA       #000
          STA       ATdataHB,x
          TAY
          RTS

                    ;D_STATUS call processing
                    ; $00   Drivers Status - always $0
                    ; $01   Return device identification - $0200 bytes long
                    ; $02   Return most recent device error information/data
                    ; $03   Return partition table data - $0100 bytes long
                    ; $04   Return DIB configuration bytes - 2 bytes long
                    ; $FE   Return preferrred bitmap location ($FFFF)

DStatus   LDA       CtlStat          ;status command
          BEQ       Status
          CMP       #001
          BEQ       S_Ident
          CMP       #002
          BEQ       ErrStat
          CMP       #003
          BEQ       ParTable
          CMP       #004
          BEQ       DIBinfo
          CMP       #0FE
          BEQ       BitMap
CS_Bad    LDA       #XCTLCODE        ;control/status code no good
Err_Out3  JSR       SysErr           ;doesn't return

                    ;Driver Status
Status    TAY
          STA       (CSList),Y
          RTS

                    ;Return partition table of current SOSunit
ParTable  JSR       GetPmap
          LDY       #000
$1        LDA       PmapBuf,Y
          STA       (CSList),Y
          INY
          BNE       $1
          RTS

                    ;Return most recent error data.
                    ; Byte 0:      Device Status Code
                    ; Byte 1:      Device Error Code  (if status ERR bit is 0,
                    ;              then error code = 0)
                    ; Byte 2,3,4:  Sector# (LB,MB,HB) error occurred
                    ; Byte 5:      # of sectors left to transfer
ErrStat   LDY       #005
$1        LDA       Err_Data,y
          STA       (CSList),Y
          DEY
          BPL       $1
          CLC
          RTS
                    ;Return DIB configuration bytes
DIBinfo   LDX       SosUnit
          LDY       DCB_Idx,X
          LDA       Part_No0,Y    ;Get assigned partition number this driver
          PHA
          LDA       Driv_No0,Y    ;Get assigned partition map/IDE device
          LDY       #000          ;for this driver.
          STA       @CSList,Y
          INY
          PLA
          STA       @CSList,Y
          CLC
          RTS

                    ;Return preferred bit map locations.  We return $FFFF
BitMap    LDY       #000
          LDA       #0FF
          STA       (CSList),Y
          INY
          STA       (CSList),Y       ;return FFFF, don't care
          CLC
          RTS       ;and leave

                    ; Device Identification
S_Ident   LDA       CSList
          STA       LoBuffer
          LDA       CSList+1
          STA       LoBuffer+1
          LDA       CSList+Extpg
          STA       LoBuffer+Extpg
          LDA       #ATAIdent
          STA       ATA_Cmd
C_Ident   LDA       #001
          STA       Num_Blks
          LDA       #000
          STA       Num_Blks+1
          JMP       SRead

                    ;Perform I/O with user supplied call block
                    ;Call Block Organization:
                    ;    Byte 0:        ATA Command Code
                    ;    Byte 1,2,3:    Sector# (HB,MB,LB absolute sector)
                    ;    Byte 4:        # of sectors
                    ;    Byte 5-6:      Bytes returned to buffer
                    ;    Byte 7...      Data Buffer

CtrlCmds  .BYTE     ATA_Xerr
          .BYTE     ATACRead
          .BYTE     ATACWrit
          .BYTE     ATA_Vrfy
          .BYTE     ATA_Frmt
          .BYTE     ATA_Diag
          .BYTE     ATAIdent

Cmd_Tbl   .WORD     Send_Cmd-1       ;Extended Error Info  $03
          .WORD     CRead-1          ;Sector Read  $20
          .WORD     CWrite-1         ;Sector Write  $30
          .WORD     Verify-1         ;Read verify  $40
          .WORD     CWrite-1         ;Sector Format  $50
          .WORD     Send_Cmd-1       ;Internal Diagnostic Test  $90
          .WORD     C_Ident-1        ;Device Identity  $EC

UserIO    LDY       #004
$1        LDA       (CSList),Y
          STA       ATA_Cmd+1,Y
          DEY
          BNE       $1
          LDA       Num_Blks         ;if zero then 256 blocks is requested
          BNE       $2
          INY
$2        STY       Num_Blks+1
          CLC                        ;Setup data addresses
          LDA       CSList
          ADC       #005
          STA       QtyRead
          LDA       CSList+1
          ADC       #000
          STA       QtyRead+1
          LDA       QtyRead
          ADC       #002
          STA       LoBuffer
          LDA       QtyRead+1
          ADC       #000
          STA       LoBuffer+1
          LDA       CSList+Extpg
          STA       QtyRead+Extpg
          STA       LoBuffer+Extpg
          LDY       #000
          LDA       (CSList),Y
          LDY       #006
$3        CMP       Ctrl_Cmds,y
          BEQ       $4
          DEY
          BPL       $3
          JMP       CS_Bad
$4        STA       ATA_Cmd
          TYA
          ASL       A
          TAY
          LDA       Cmd_Tbl+1,Y
          PHA
          LDA       Cmd_Tbl,Y
          PHA
          RTS

                    ;D_CONTROL call processing
                    ; $00     Reset device
                    ; $01     Perform device I/O function with user supplied
                    ;         call block.
                    ; $02     Set Device Features
                    ; $04     Set DIB configuration bytes
                    ; $FE     Perform media formatting

DControl  LDA       CtlStat          ;control command
          BEQ       CReset
          CMP       #001
          BEQ       UserIO
          CMP       #002
          BEQ       C_Featur
          CMP       #004
          BEQ       New_DIB
          CMP       #0FE             ;formatting?
          BEQ       MFormat
          JMP       CS_Bad           ;Control code no good!

CReset    JSR       ResetIFC         ;Reset CFFA card
          BCS       $1
          RTS
$1        LDA       #XNORESET
          JSR       SysErr           ;doesn't return

                    ;Execute media formatting call.
MFormat   LDX       #007
$1        LDY       DCB_Idx,X
          LDA       Driv_No0,Y
          CMP       CurDrvNo
          BNE       $2
          LDA       #0FF           ;Invalidate partition table status
          STA       UnitStat,X     ;so subsequent read/writes
$2        DEX                      ;will re-initialize the partition info
          BPL       $1             ;for each driver designated for this
          CLC                      ;partiton table.
          RTS

                    ;Save new DIB configuration bytes
New_DIB   LDX       SosUnit
          LDA       #0FF           ;Invalidate partition table status of driver
          STA       UnitStat,X
          LDY       #000
          LDA       @CSList,Y
          PHA
          INY
          LDA       @CSList,Y
          LDY       DCB_Idx,X
          STA       Part_No0,Y    ;Get assigned partition number this driver
          PLA
          STA       Driv_No0,Y    ;Get assigned partition map/IDE device
          CLC                     ;for this driver.
          RTS

                    ;Set Device Features Command
                    ;Call Block Organization:
                    ;    Byte 0:        Feature Code
                    ;    Byte 1:        PIO_Mode (for Set Transfer Mode
                    ;                              feature only).

C_Featur  LDY       #000
          LDA       @CSList,Y
          STA       FR_Code
          CMP       #003                 ;check if feature code is set
          BNE       D_Featur             ;transfer mode $03.  No, continue to
          INY                            ;set feature.
          LDA       @CSList,Y
          STA       PIO_Mode             ;Save PIO mode for all subsequent
D_Featur  JSR       CkDevice             ;transfer settings.
          LDA       FR_Code
          STA       Features,x
          CMP       #003                 ;check if feature code is set
          BNE       $1                   ;transfer mode $03.  No, continue to
          LDA       PIO_Mode             ;set feature.  Yes, get PIO_Mode
          STA       ATSectCt,x           ;value.
$1        LDA       #SetFeatr
          STA       ATCmdReg,x           ;Issue the Set Features command
$2        LDA       ATA_Stat,x
          BMI       $2
          LDA       ATAError,x
          STA       Err_Data
          AND       #004
          JSR       Set2Mhz
          BNE       $3
          RTS
$3        LDA       #XDCMDERR            ;$31  device command ABORTED error
          JSR       SysErr               ;occurred -> doesn't return.

                    ;Supplemental Subroutines

                    ;Device Internal Diagnostic Routine  ATA Command $90
                    ; Returns 1 byte of diagnostic code in Buffer
                    ; $01 = No Error Detected
                    ; $02 = Formatter Device Error
                    ; $03 = Sector Buffer Error
                    ; $04 = ECC Circuitry Error
                    ; $05 = Controlling Microprocessor Error
                    ; $8x = Slave Failed (true IDE mode)

                    ;Extended Error Code Request
                    ; Returns 1 byte of exteded error code in Buffer
                    ; $00 = No Error Detected
                    ; $01 = Self test OK (No error)
                    ; $09 = Miscellaneous Error
                    ; $20 = Invalid Command
                    ; $21 = Invalid address (requested head or sector invalid)
                    ; $2F = Address Overflow (address too large)
                    ; $35,$36 = Supply voltage out of tolerance
                    ; $11 = Uncorrectable ECC error
                    ; $18 = Corrected ECC Error
                    ; $05,$30-34,$37,$3E = Self test or diagnostic failed
                    ; $10,$14 = ID not found
                    ; $3A = Spare sectors exhausted
                    ; $1F = Data transfer error/Aborted command
                    ; $0C,$38,$3B,$3C,$3F = Corrupted Media Format
                    ; $03 = Write/Erase failed

Send_Cmd  JSR       CkDevice
          LDA       #000
          STA       ATdataHB,x       ;Clear high byte data latch
          LDA       ATA_Cmd
          STA       ATCmdReg,x       ;Issue the ATA command to the drive
$1        LDA       ATA_Stat,x
          BMI       $1
          LDA       ATAError,x
          LDY       #000
          STA       (LoBuffer),y
          JMP       CSet2Mhz

                    ;Verify - Verify requested blocks
Verify    JSR       SetupLBA         ;Program the device's task file registers
          LDA       #ATA_Vrfy
          STA       ATCmdReg,x       ;Issue the read verify command to drive
$1        LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       $1
          LSR       A
          BCS       $2
          JMP       Set2Mhz
$2        JMP       Writ_Err

                    ;Execute reset call to ATA device
ResetIFC  JSR       Set1Mhz
          LDX       SlotCX
          LDA       ClrCSMsk,x      ;reset MASK bit in PLD for normal CS0
          LDA       #000            ;signaling.
          STA       ATdataHB,x      ;Clear high byte data latch
          LDA       #006            ;Reset bit=1, Disable INTRQ=1
          STA       ATAdCtrl,x
          JSR       Norm_Out        ;Per ATA-6 spec, need to wait 5us minimum.
          LDA       #002            ;Reset bit=0, Disable INTRQ=1
          STA       ATAdCtrl,x
                                    ;Per ATA-6 spec, need to wait 2ms minimum
          LDY       #208.
          LDA       #Wait5ms        ;Use a initial delay of 5ms.
$1        JSR       Wait
          LDA       ATA_Stat,x      ;Per ATA-6 spec, wait up to 31 sec for
          BMI       $2              ;busy to clear if a slave is attached.
          JMP       CSet2Mhz
$2        LDA       #Wait150ms      ;Wait for up to 31 secs if a slave is
          DEY                       ;attached.  After 31 secs pass, the card
          BNE       $1              ;is not installed in slot.
          SEC                       ;Drive(s) Not Ready
          JMP       Set2Mhz

                    ;Check Device - test drive status register is readable
                    ;and equal to $40
CkDevice  JSR       Set1Mhz
          LDY       #200.
          LDX       SlotCX
          LDA       ClrCSMsk,x       ;reset MASK bit in PLD for normal CS0
$1        LDA       ATA_Stat,x       ;signaling.
          BMI       $1
          AND       #008             ;%00001000  Check bus can receive a
          BEQ       $2               ;device register setting DRQ=0
          LDA       #Wait5ms
          JSR       Wait             ;Wait 5ms to try again
          DEY
          BNE       $1              ;Wait up to 1 second for drive to be ready
          BEQ       NotReady        ;always taken
$2        LDA       Drv_Parm        ;Select drive to check
          STA       ATAHead,x
          LDY       #200.
$3        LDA       ATA_Stat,x
          BMI       $3
          AND       #0E9             ;%11101001  Check if device is ready to
          CMP       #040             ;receive a command.  If BSY=0, RDY=1,
          BNE       $4               ;DF=0, DRQ=0, and ERR=0
          RTS                        ;We're good to go.  Returns @ 1 Mhz clock
                                     ;Xreg = SlotCX, and carry set.
$4        LDA       #Wait5ms
          JSR       Wait             ;Wait 5ms to try again
          DEY
          BNE       $3               ;Wait up to 1 seconds for drive readiness
NotReady  JSR       Set2Mhz
          LDA       #XCKDEVER        ;DEVICE NOT READY error
          JSR       SysErr

                    ; Wait - Copy of Apple's wait routine.
                    ; Input:
                    ;A =  delay time, where Delay(us) = 2.5A^2 + 13.5A + 36
                    ;     including JSR to this routine.
                    ;or more typically A = (Delay[in uS]/2.5 - 7.11)^.5 - 2.7
Wait      PHP
          SEI
          SEC
$1        PHA
$2        SBC       #001
          BNE       $2
          PLA
          SBC       #001
          BNE       $1
          PLP
          RTS

Set1Mhz   PHP       ;Throttle back to 1 Mhz
          SEI
          LDA       EReg
          ORA       #080
          STA       EReg
          PLP
          RTS

CSet2Mhz  CLC       ;Throttle up to 2 Mhz
Set2Mhz   PHP
          SEI
          LDA       EReg
          AND       #07F
          STA       EReg
          PLP
          RTS

